To be able to edit code and run cells, you need to run the notebook yourself. Where would you like to run the notebook?

In the cloud (experimental)

Binder is a free, open source service that runs scientific notebooks in the cloud! It will take a while, usually 2-7 minutes to get a session.

On your computer

(Recommended if you want to store your changes.)

  1. Copy the notebook URL:
  2. Run Pluto

    (Also see: How to install Julia and Pluto)

  3. Paste URL in the Open box

Frontmatter

If you are publishing this notebook on the web, you can set the parameters below to provide HTML metadata. This is useful for search engines and social media.

Author 1

homework 1, version 0

👀 Reading hidden code
19.9 ms

Submission by: SOLUTIONS (SOLUTIONS@mit.edu)

👀 Reading hidden code
11.2 ms

Homework 1 - convolutions

18.S191, fall 2020

This notebook contains built-in, live answer checks! In some exercises you will see a coloured box, which runs a test case on your code, and provides feedback based on the result. Simply edit the code, run it, and the check runs again.

For MIT students: there will also be some additional (secret) test cases that will be run as part of the grading process, and we will look at your notebook and write comments.

Feel free to ask questions!

👀 Reading hidden code
59.4 ms
👀 Reading hidden code
# edit the code below to set your name and kerberos ID (i.e. email without @mit.edu)

student = (name = "SOLUTIONS", kerberos_id = "SOLUTIONS")

# press the ▶ button in the bottom right of this cell to run your edits
# or use Shift+Enter

# you might need to wait until all other cells in this notebook have completed running.
# scroll down the page to see what's up
17.0 μs

Let's create a package environment:

👀 Reading hidden code
291 μs
👀 Reading hidden code
begin
import Pkg
Pkg.activate(mktempdir())
end
❔
  Activating new project at `/tmp/jl_jKLOp0`
139 ms

We set up Images.jl again:

👀 Reading hidden code
223 μs
begin
Pkg.add(["Images", "ImageIO", "ImageMagick"])
using Images
end
👀 Reading hidden code
❔
    Updating registry at `~/.julia/registries/General.toml`
   Resolving package versions...
    Updating `/tmp/jl_jKLOp0/Project.toml`
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.0
  [916415d5] + Images v0.26.2
    Updating `/tmp/jl_jKLOp0/Manifest.toml`
  [621f4979] + AbstractFFTs v1.5.0
  [79e6a3ab] + Adapt v3.7.2
  [66dad0bd] + AliasTables v1.1.3
  [ec485272] + ArnoldiMethod v0.4.0
  [4fba245c] + ArrayInterface v7.5.1
  [13072b0f] + AxisAlgorithms v1.1.0
  [39de3d68] + AxisArrays v0.4.7
  [62783981] + BitTwiddlingConvenienceFunctions v0.1.6
  [fa961155] + CEnum v0.5.0
  [2a0fbf3d] + CPUSummary v0.2.6
  [aafaddc9] + CatIndices v0.2.2
  [d360d2e6] + ChainRulesCore v1.25.1
  [9e997f8a] + ChangesOfVariables v0.1.9
  [fb6a15b2] + CloseOpenIntervals v0.1.13
  [aaaa29a8] + Clustering v0.15.8
  [35d6a980] + ColorSchemes v3.29.0
  [3da002f7] + ColorTypes v0.12.0
  [c3611d14] + ColorVectorSpace v0.11.0
  [5ae59095] + Colors v0.13.0
  [bbf7d656] + CommonSubexpressions v0.3.1
  [34da2185] + Compat v4.16.0
  [ed09eef8] + ComputationalResources v0.3.2
  [187b0558] + ConstructionBase v1.5.8
  [150eb455] + CoordinateTransformations v0.6.3
  [adafc99b] + CpuId v0.3.1
  [dc8bdbbb] + CustomUnitRanges v1.0.2
  [9a962f9c] + DataAPI v1.16.0
  [864edb3b] + DataStructures v0.18.20
  [163ba53b] + DiffResults v1.1.0
  [b552c78f] + DiffRules v1.15.1
  [b4f34e82] + Distances v0.10.12
  [ffbed154] + DocStringExtensions v0.9.3
  [4f61f5a4] + FFTViews v0.3.2
  [7a1cc6ca] + FFTW v1.8.1
  [5789e2e9] + FileIO v1.17.0
  [53c48c17] + FixedPointNumbers v0.8.5
  [f6369f11] + ForwardDiff v0.10.38
  [a2bd30eb] + Graphics v1.1.3
  [86223c79] + Graphs v1.12.0
  [2c695a8d] + HistogramThresholding v0.3.1
  [3e5b6fbb] + HostCPUFeatures v0.1.17
  [615f187c] + IfElse v0.1.1
  [2803e5a7] + ImageAxes v0.6.12
  [c817782e] + ImageBase v0.1.7
  [cbc4b850] + ImageBinarization v0.3.1
  [f332f351] + ImageContrastAdjustment v0.3.12
  [a09fc81d] + ImageCore v0.10.5
  [89d5987c] + ImageCorners v0.1.3
  [51556ac3] + ImageDistances v0.2.17
  [6a3955dd] + ImageFiltering v0.7.9
  [82e4d734] + ImageIO v0.6.9
  [6218d12a] + ImageMagick v1.4.0
  [bc367c6b] + ImageMetadata v0.9.10
  [787d08f9] + ImageMorphology v0.4.5
  [2996bd0c] + ImageQualityIndexes v0.3.7
  [80713f31] + ImageSegmentation v1.8.4
  [4e3cecfd] + ImageShow v0.3.8
  [02fcd773] + ImageTransformations v0.10.1
  [916415d5] + Images v0.26.2
  [9b13fd28] + IndirectArrays v1.0.0
  [d25df0c9] + Inflate v0.1.5
  [1d092043] + IntegralArrays v0.1.6
  [a98d9a8b] + Interpolations v0.15.1
  [8197267c] + IntervalSets v0.7.10
  [3587e190] + InverseFunctions v0.1.17
  [92d709cd] + IrrationalConstants v0.2.4
  [c8e1da08] + IterTools v1.4.0
  [033835bb] + JLD2 v0.5.11
  [692b3bcd] + JLLWrappers v1.7.0
  [b835a17e] + JpegTurbo v0.1.5
  [10f19ff3] + LayoutPointers v0.1.17
  [8cdb02fc] + LazyModules v0.3.1
  [2ab3a3ac] + LogExpFunctions v0.3.28
  [bdcacae8] + LoopVectorization v0.12.171
  [1914dd2f] + MacroTools v0.5.15
  [d125e4d3] + ManualMemory v0.1.8
  [dbb5928d] + MappedArrays v0.4.2
  [626554b9] + MetaGraphs v0.8.0
  [e1d29d7a] + Missings v1.2.0
  [e94cdb99] + MosaicViews v0.3.4
  [77ba4419] + NaNMath v1.0.3
  [b8a86587] + NearestNeighbors v0.4.21
  [f09324ee] + Netpbm v1.1.1
  [6fe1bfb0] + OffsetArrays v1.15.0
  [52e1d378] + OpenEXR v0.3.3
  [bac558e1] + OrderedCollections v1.8.0
  [f57f5aa1] + PNGFiles v0.4.4
  [5432bcbf] + PaddedViews v0.5.12
  [d96e819e] + Parameters v0.12.3
  [eebad327] + PkgVersion v0.3.3
  [1d0040c9] + PolyesterWeave v0.2.2
  [f27b6e38] + Polynomials v4.0.19
  [aea7be01] + PrecompileTools v1.2.1
  [21216c6a] + Preferences v1.4.3
  [92933f4c] + ProgressMeter v1.10.2
  [43287f4e] + PtrArrays v1.3.0
  [4b34888f] + QOI v1.0.1
  [94ee1d12] + Quaternions v0.7.6
  [b3c3ace0] + RangeArrays v0.3.2
  [c84ed2f1] + Ratios v0.4.5
  [c1ae055f] + RealDot v0.1.0
  [3cdcf5f2] + RecipesBase v1.3.4
  [189a3867] + Reexport v1.2.2
  [dee08c22] + RegionTrees v0.3.2
  [ae029012] + Requires v1.3.1
  [6038ab10] + Rotations v1.7.1
  [fdea26ae] + SIMD v3.7.1
  [94e857df] + SIMDTypes v0.1.0
  [476501e8] + SLEEFPirates v0.6.43
  [efcf1570] + Setfield v1.1.2
  [699a6c99] + SimpleTraits v0.9.4
  [47aef6b3] + SimpleWeightedGraphs v1.4.0
  [45858cf5] + Sixel v0.1.3
  [a2af1166] + SortingAlgorithms v1.2.1
  [276daf66] + SpecialFunctions v2.5.0
  [cae243ae] + StackViews v0.1.1
  [aedffcd0] + Static v0.8.9
  [0d7ed370] + StaticArrayInterface v1.6.0
  [90137ffa] + StaticArrays v1.9.13
  [1e83bf80] + StaticArraysCore v1.4.3
  [82ae8749] + StatsAPI v1.7.0
  [2913bbd2] + StatsBase v0.34.4
  [62fd8b95] + TensorCore v0.1.1
  [8290d209] + ThreadingUtilities v0.5.2
  [731e570b] + TiffImages v0.11.3
  [06e1c1a7] + TiledIteration v0.5.0
  [3bb67fe8] + TranscodingStreams v0.11.3
  [3a884ed6] + UnPack v1.0.2
  [3d5dd08c] + VectorizationBase v0.21.71
  [e3aaa7dc] + WebP v0.1.3
  [efce3f68] + WoodburyMatrices v1.0.0
  [f5851436] + FFTW_jll v3.3.10+3
  [61579ee1] + Ghostscript_jll v9.55.0+4
  [59f7168a] + Giflib_jll v5.2.3+0
  [c73af94c] + ImageMagick_jll v7.1.1+1
  [905a6f67] + Imath_jll v3.1.11+0
  [1d5cc7b8] + IntelOpenMP_jll v2025.0.4+0
  [aacddb02] + JpegTurbo_jll v3.1.1+0
  [88015f11] + LERC_jll v3.0.0+1
  [d4300ac3] + Libgcrypt_jll v1.11.0+0
  [7e76a0d4] + Libglvnd_jll v1.7.0+0
  [7add5ba3] + Libgpg_error_jll v1.51.1+0
  [94ce4f54] + Libiconv_jll v1.18.0+0
  [89763e89] + Libtiff_jll v4.4.0+0
  [d3a379c0] + LittleCMS_jll v2.12.0+0
  [856f044c] + MKL_jll v2025.0.1+1
  [18a262bb] + OpenEXR_jll v3.2.4+0
  [643b3616] + OpenJpeg_jll v2.4.0+0
  [efe28fd5] + OpenSpecFun_jll v0.5.6+0
  [02c8fc9c] + XML2_jll v2.13.6+1
  [aed1982a] + XSLT_jll v1.1.42+0
  [4f6342f7] + Xorg_libX11_jll v1.8.6+3
  [0c0b7dd1] + Xorg_libXau_jll v1.0.12+0
  [a3789734] + Xorg_libXdmcp_jll v1.1.5+0
  [1082639a] + Xorg_libXext_jll v1.3.6+3
  [14d82f49] + Xorg_libpthread_stubs_jll v0.1.2+0
  [c7cfdc94] + Xorg_libxcb_jll v1.17.0+3
  [c5fb5394] + Xorg_xtrans_jll v1.5.1+0
  [3161d3a3] + Zstd_jll v1.5.7+1
  [b53b4c65] + libpng_jll v1.6.47+0
  [075b6546] + libsixel_jll v1.10.5+0
  [c5f90fcd] + libwebp_jll v1.4.0+0
  [1317d2d5] + oneTBB_jll v2022.0.0+0
  [0dad84c5] + ArgTools
  [56f22d72] + Artifacts
  [2a0f44e3] + Base64
  [ade2ca70] + Dates
  [8ba89e20] + Distributed
  [f43a241f] + Downloads
  [7b1f6079] + FileWatching
  [9fa8497b] + Future
  [b77e0a4c] + InteractiveUtils
  [4af54fe1] + LazyArtifacts
  [b27032c2] + LibCURL
  [76f85450] + LibGit2
  [8f399da3] + Libdl
  [37e2e46d] + LinearAlgebra
  [56ddb016] + Logging
  [d6f4376e] + Markdown
  [a63ad114] + Mmap
  [ca575930] + NetworkOptions
  [44cfe95a] + Pkg
  [de0858da] + Printf
  [3fa0cd96] + REPL
  [9a3f8284] + Random
  [ea8e919c] + SHA
  [9e88b42a] + Serialization
  [1a1011a3] + SharedArrays
  [6462fe0b] + Sockets
  [2f01184e] + SparseArrays
  [10745b16] + Statistics
  [4607b0f0] + SuiteSparse
  [fa267f1f] + TOML
  [a4e569a6] + Tar
  [8dfed614] + Test
  [cf7118a7] + UUIDs
  [4ec0a83e] + Unicode
  [e66e0078] + CompilerSupportLibraries_jll
  [deac9b47] + LibCURL_jll
  [29816b5a] + LibSSH2_jll
  [c8ffd9c3] + MbedTLS_jll
  [14a3606d] + MozillaCACerts_jll
  [4536629a] + OpenBLAS_jll
  [05823500] + OpenLibm_jll
  [83775a58] + Zlib_jll
  [8e850b90] + libblastrampoline_jll
  [8e850ede] + nghttp2_jll
  [3f19e933] + p7zip_jll
10.2 s





👀 Reading hidden code
9.4 μs

Exercise 1 - Manipulating vectors (1D images)

A Vector is a 1D array. We can think of that as a 1D image.

👀 Reading hidden code
5.4 ms
👀 Reading hidden code
14.7 μs
colored_line(example_vector)
👀 Reading hidden code
16.8 μs

Exerise 1.1

👉 Make a random vector random_vect of length 10 using the rand function.

👀 Reading hidden code
5.1 ms
random_vect = rand(10)
👀 Reading hidden code
15.6 μs
👀 Reading hidden code
16.2 μs

Got it!

Yay ❤

👀 Reading hidden code
19.5 ms

Hint

You can find out more about any function (like rand) by creating a new cell and typing:

?rand

Once the Live Docs are open, you can select any code to learn more about it. It might be useful to leave it open all the time, and get documentation while you type code.

👀 Reading hidden code
46.0 ms

👉 Make a function mean using a for loop, which computes the mean/average of a vector of numbers.

👀 Reading hidden code
255 μs
mean (generic function with 1 method)
function mean(x)
return sum(x) / length(x)
end
👀 Reading hidden code
439 μs
2.0
mean([1, 2, 3])
👀 Reading hidden code
15.2 μs

Got it!

Great! 🎉

👀 Reading hidden code
176 μs

👉 Define m to be the mean of random_vect.

👀 Reading hidden code
223 μs
0.6293489284489368
m = mean(random_vect)
👀 Reading hidden code
14.5 μs

Got it!

Great! 🎉

👀 Reading hidden code
120 μs

👉 Write a function demean, which takes a vector x and subtracts the mean from each value in x.

👀 Reading hidden code
209 μs
demean (generic function with 1 method)
function demean(x)
return x .- mean(x)
end
👀 Reading hidden code
458 μs

Let's check that the mean of the demean(random_vect) is 0:

Due to floating-point round-off error it may not be exactly 0.

👀 Reading hidden code
7.9 ms
👀 Reading hidden code
2.5 ms
3.3306690738754695e-17
mean(demean(copy_of_random_vect))
👀 Reading hidden code
13.9 μs
copy_of_random_vect = copy(random_vect); # in case demean modifies `x`
👀 Reading hidden code
14.4 μs

Exercise 1.2

👉 Generate a vector of 100 zeros. Change the center 20 elements to 1.

👀 Reading hidden code
282 μs
create_bar (generic function with 1 method)
function create_bar()
x = zeros(100)
x[40:59] .= 1
return x
end
👀 Reading hidden code
526 μs
👀 Reading hidden code
18.9 μs

Got it!

Great! 🎉

👀 Reading hidden code
5.4 ms

Exercise 1.3

👉 Write a function that turns a Vector of Vectors into a Matrix.

👀 Reading hidden code
262 μs
vecvec_to_matrix (generic function with 1 method)
function vecvec_to_matrix(vecvec)
return hcat(vecvec...)
end
👀 Reading hidden code
403 μs
2×2 Matrix{Int64}:
 1  3
 2  4
vecvec_to_matrix([[1,2], [3,4]])
👀 Reading hidden code
41.7 ms

Got it!

Great!

👀 Reading hidden code
46.9 ms

👉 Write a function that turns a Matrix into aVector of Vectors .

👀 Reading hidden code
228 μs
matrix_to_vecvec (generic function with 1 method)
function matrix_to_vecvec(matrix)
return collect(eachcol(matrix))
end
👀 Reading hidden code
398 μs
matrix_to_vecvec([6 7; 8 9])
👀 Reading hidden code
26.6 μs

Got it!

Great! 🎉

👀 Reading hidden code
134 μs
colored_line (generic function with 2 methods)
👀 Reading hidden code
881 μs





👀 Reading hidden code
8.5 μs

Exercise 2 - Manipulating images

In this exercise we will get familiar with matrices (2D arrays) in Julia, by manipulating images. Recall that in Julia images are matrices of RGB color objects.

Let's load a picture of Philip again.

👀 Reading hidden code
399 μs
"/tmp/jl_wcFqmy"
philip_file = download("https://i.imgur.com/VGPeJ6s.jpg")
👀 Reading hidden code
283 ms
philip = let
original = Images.load(philip_file)
decimate(original, 8)
end
👀 Reading hidden code
738 ms

Hi there Philip

👀 Reading hidden code
214 μs

Exercise 2.1

👉 Write a function mean_colors that accepts an object called image. It should calculate the mean (average) amounts of red, green and blue in the image and return a tuple (r, g, b) of those means.

👀 Reading hidden code
298 μs
mean_colors (generic function with 1 method)
function mean_colors(image)
c = mean(image)
c.r, c.g, c.b
end
👀 Reading hidden code
546 μs
mean_colors(philip)
👀 Reading hidden code
84.5 μs

Got it!

Great!

👀 Reading hidden code
65.6 ms

Exercise 2.2

👉 Look up the documentation on the floor function. Use it to write a function quantize(x::Number) that takes in a value x (which you can assume is between 0 and 1) and "quantizes" it into bins of width 0.1. For example, check that 0.267 gets mapped to 0.2.

👀 Reading hidden code
6.1 ms
quantize (generic function with 3 methods)
begin
function quantize(x::Number)
return floor(10x) / 10
end
function quantize(color::AbstractRGB)
return RGB(quantize(color.r), quantize(color.g), quantize(color.b))
end
function quantize(image::AbstractMatrix)
return quantize.(image)
end
end
👀 Reading hidden code
1.2 ms
quantize(0.267), quantize(0.91)
👀 Reading hidden code
14.8 μs

Got it!

Let's move on to the next section.

👀 Reading hidden code
121 μs

Exercise 2.3

👉 Write the second method of the function quantize, i.e. a new version of the function with the same name. This method will accept a color object called color, of the type AbstractRGB.

Write the function in the same cell as quantize(x::Number) from the last exercise. 👆

Here, ::AbstractRGB is a type annotation. This ensures that this version of the function will be chosen when passing in an object whose type is a subtype of the AbstractRGB abstract type. For example, both the RGB and RGBX types satisfy this.

The method you write should return a new RGB object, in which each component (r, g and b) are quantized.

👀 Reading hidden code
725 μs

Exercise 2.4

👉 Write a method quantize(image::AbstractMatrix) that quantizes an image by quantizing each pixel in the image. (You may assume that the matrix is a matrix of color objects.)

Write the function in the same cell as quantize(x::Number) from the last exercise. 👆

👀 Reading hidden code
348 μs

Let's apply your method!

👀 Reading hidden code
206 μs
quantize(philip)
👀 Reading hidden code
2.2 ms

Exercise 2.5

👉 Write a function invert that inverts a color, i.e. sends (r,g,b) to (1r,1g,1b).

👀 Reading hidden code
255 μs
invert (generic function with 1 method)
function invert(color::AbstractRGB)
return RGB(1-color.r, 1-color.g, 1-color.b)
end
👀 Reading hidden code
603 μs

Let's invert some colors:

👀 Reading hidden code
180 μs
👀 Reading hidden code
13.1 μs
invert(black)
👀 Reading hidden code
11.8 μs
👀 Reading hidden code
13.6 μs
invert(red)
👀 Reading hidden code
11.8 μs

Can you invert the picture of Philip?

👀 Reading hidden code
200 μs
philip_inverted = invert.(philip)
👀 Reading hidden code
67.1 ms

Exercise 2.6

👉 Write a function noisify(x::Number, s) to add randomness of intensity s to a value x, i.e. to add a random value between s and +s to x. If the result falls outside the range (0,1) you should "clamp" it to that range. (Note that Julia has a clamp function, but you should write your own function myclamp(x).)

👀 Reading hidden code
323 μs
noisify (generic function with 3 methods)
begin
function noisify(x::Number, s)
return x + s*(rand()*2 - 1)
end
function noisify(color::AbstractRGB, s)
return RGB(noisify(color.r, s), noisify(color.g, s), noisify(color.b, s))
end
function noisify(image::AbstractMatrix, s)
return noisify.(image, [s])
end
end
👀 Reading hidden code
1.3 ms

Hint

The rand function generates (uniform) random floating-point numbers between 0 and 1.

👀 Reading hidden code
287 μs

👉 Write the second method noisify(c::AbstractRGB, s) to add random noise of intensity s to each of the (r,g,b) values in a colour.

Write the function in the same cell as noisify(x::Number) from the last exercise. 👆

👀 Reading hidden code
303 μs
0.0
👀 Reading hidden code
289 ms
noisify(red, color_noise)
👀 Reading hidden code
13.6 μs
👀 Reading hidden code
59.5 μs

👉 Write the third method noisify(image::AbstractMatrix, s) to noisify each pixel of an image.

Write the function in the same cell as noisify(x::Number) from the last exercise. 👆

👀 Reading hidden code
287 μs
0.0
@bind philip_noise Slider(0:0.01:8, show_value=true)
👀 Reading hidden code
798 μs
noisify(philip, philip_noise)
👀 Reading hidden code
2.3 ms
👀 Reading hidden code
61.3 μs

👉 For which noise intensity does it become unrecognisable?

You may need noise intensities larger than 1. Why?

👀 Reading hidden code
228 μs

The image is unrecognisable with intensity 3

The information density is lower than the pixel density - you can visually combine blurry neighbouring pixels to still form an image.

answer_about_noise_intensity = md"""
The image is unrecognisable with intensity 3

The information density is lower than the pixel density - you can visually combine blurry neighbouring pixels to still form an image.
"""
👀 Reading hidden code
303 μs
👀 Reading hidden code
59.5 μs
begin
Pkg.add("PlutoUI")
using PlutoUI
end
👀 Reading hidden code
❔
   Resolving package versions...
    Updating `/tmp/jl_jKLOp0/Project.toml`
  [7f904dfe] + PlutoUI v0.7.23
    Updating `/tmp/jl_jKLOp0/Manifest.toml`
  [6e696c72] + AbstractPlutoDingetjes v1.3.2
  [47d2ed2b] + Hyperscript v0.0.4
  [ac1192a8] + HypertextLiteral v0.9.5
  [b5f81e59] + IOCapture v0.2.5
  [682c06a0] + JSON v0.21.4
  [69de0a69] + Parsers v2.8.1
  [7f904dfe] + PlutoUI v0.7.23
  [410a4b4d] + Tricks v0.1.10
4.0 s
decimate (generic function with 2 methods)
👀 Reading hidden code
709 μs





👀 Reading hidden code
9.3 μs

Exercise 3 - Convolutions

As we have seen in the videos, we can produce cool effects using the mathematical technique of convolutions. We input one image M and get a new image M back.

Conceptually we think of M as a matrix. In practice, in Julia it will be a Matrix of color objects, and we may need to take that into account. Ideally, however, we should write a generic function that will work for any type of data contained in the matrix.

A convolution works on a small window of an image, i.e. a region centered around a given point (i,j). We will suppose that the window is a square region with odd side length 2+1, running from ,,0,,.

The result of the convolution over a given window, centred at the point (i,j) is a single number; this number is the value that we will use for Mi,j. (Note that neighbouring windows overlap.)

To get started let's restrict ourselves to convolutions in 1D. So a window is just a 1D region from to .

👀 Reading hidden code
742 μs

Let's create a vector v of random numbers of length n=100.

👀 Reading hidden code
214 μs
100
n = 100
👀 Reading hidden code
11.6 μs
v = rand(n)
👀 Reading hidden code
16.3 μs

Feel free to experiment with different values!

👀 Reading hidden code
281 μs

Exercise 3.1

You've seen some colored lines in this notebook to visualize arrays. Can you make another one?

👉 Try plotting our vector v using colored_line(v).

👀 Reading hidden code
303 μs
colored_line(v)
👀 Reading hidden code
16.8 μs

Try changing n and v around. Notice that you can run the cell v = rand(n) again to regenerate new random values.

👀 Reading hidden code
208 μs

Exercise 3.2

We need to decide how to handle the boundary conditions, i.e. what happens if we try to access a position in the vector v beyond 1:n. The simplest solution is to assume that vi is 0 outside the original vector; however, this may lead to strange boundary effects.

A better solution is to use the closest value that is inside the vector. Effectively we are extending the vector and copying the extreme values into the extended positions. (Indeed, this is one way we could implement this; these extra positions are called ghost cells.)

👉 Write a function extend(v, i) that checks whether the position i is inside 1:n. If so, return the ith component of v; otherwise, return the nearest end value.

👀 Reading hidden code
545 μs
extend (generic function with 1 method)
function extend(v, i)
if i < 1
v[1]
elseif i > length(v)
v[end]
else
v[i]
end
end
👀 Reading hidden code
639 μs

Some test cases:

👀 Reading hidden code
212 μs
0.7814714745396361
extend(v, 1)
👀 Reading hidden code
12.8 μs
0.7814714745396361
extend(v, -8)
👀 Reading hidden code
12.5 μs
0.0988333788117135
extend(v, n + 10)
👀 Reading hidden code
14.9 μs

Extended with 0:

👀 Reading hidden code
181 μs
👀 Reading hidden code
40.0 ms

Extended with your extend:

👀 Reading hidden code
186 μs
👀 Reading hidden code
36.6 μs

Got it!

Well done!

👀 Reading hidden code
103 μs

Exercise 3.3

👉 Write a function blur_1D(v, l) that blurs a vector v with a window of length l by averaging the elements within a window from to . This is called a box blur.

👀 Reading hidden code
319 μs
blur_1D (generic function with 1 method)
function blur_1D(v, l)
return map(eachindex(v)) do i
mean([extend(v, i+j) for j in -l:l])
end
end
👀 Reading hidden code
1.5 ms
👀 Reading hidden code
78.6 ms

Exercise 3.4

👉 Apply the box blur to your vector v. Show the original and the new vector by creating two cells that call colored_line. Make the parameter interactive.

👀 Reading hidden code
251 μs
0
👀 Reading hidden code
45.8 ms
colored_line(blur_1D(v, l))
👀 Reading hidden code
22.0 μs

Hint

Have a look at Exercise 2 to see an example of adding interactivity with a slider. You can read the Interactivity and the PlutoUI sample notebooks (right click -> Open in new tab) to learn more.

👀 Reading hidden code
370 μs

Exercise 3.5

The box blur is a simple example of a convolution, i.e. a linear function of a window around each point, given by

vi=nvinkn,

where k is a vector called a kernel.

Again, we need to take care about what happens if vin falls off the end of the vector.

👉 Write a function convolve_vector(v, k) that performs this convolution. You need to think of the vector k as being centred on the position i. So n in the above formula runs between and , where 2+1 is the length of the vector k. You will need to do the necessary manipulation of indices.

👀 Reading hidden code
578 μs
convolve_vector (generic function with 1 method)
function convolve_vector(v, k)
l = (length(k) - 1) ÷ 2
return map(eachindex(v)) do i
sum([extend(v, i - j) * k[j + 1 + l] for j in -l:l])
end
end
👀 Reading hidden code
1.8 ms

Hint

l = (length(k) - 1) ÷ 2

👀 Reading hidden code
215 μs
👀 Reading hidden code
15.7 μs
test_convolution = let
v = [1, 10, 100, 1000, 10000]
k = [1, 1, 0]
convolve_vector(v, k)
end
👀 Reading hidden code
32.0 μs

Edit the cell above, or create a new cell with your own test cases!

👀 Reading hidden code
727 μs

Got it!

Good job!

👀 Reading hidden code
240 μs

Exercise 3.6

👉 Write a function gaussian_kernel.

👀 Reading hidden code
268 μs
pascal (generic function with 1 method)
function pascal(n)
if n <= 1
[1.0]
else
prev = pascal(n - 1)
0.5 * ([prev..., 0] .+ [0, prev...])
end
end
👀 Reading hidden code
765 μs
gaussian_kernel (generic function with 1 method)
function gaussian_kernel(n)
pascal(2n+1)
end
👀 Reading hidden code
430 μs
gaussian_kernel.(0:5)
👀 Reading hidden code
584 ms

Let's test your kernel function!

👀 Reading hidden code
198 μs
@bind gaussian_kernel_size_1D Slider(0:6)
👀 Reading hidden code
7.3 ms
👀 Reading hidden code
14.6 μs
test_gauss_1D_a = let
v = random_vect
k = gaussian_kernel(gaussian_kernel_size_1D)
if k !== missing
convolve_vector(v, k)
end
end
👀 Reading hidden code
63.4 ms
👀 Reading hidden code
14.3 μs
test_gauss_1D_b = let
v = create_bar()
k = gaussian_kernel(gaussian_kernel_size_1D)
if k !== missing
convolve_vector(v, k)
end
end
👀 Reading hidden code
46.4 μs





👀 Reading hidden code
9.0 μs

Exercise 4 - Convolutions of images

Now let's move to 2D images. The convolution is then given by a kernel matrix K:

Mi,j=k,lMik,jlKk,l,

where the sum is over the possible values of k and l in the window. Again we think of the window as being centered at (i,j).

👀 Reading hidden code
493 μs

Exercise 4.1

👉 Write a function extend_mat that takes a matrix M and indices i and j, and returns the closest element of the matrix.

👀 Reading hidden code
257 μs
extend_mat (generic function with 1 method)
function extend_mat(M::AbstractMatrix, i, j)
if i < 1
extend_mat(M, 1, j)
elseif i > size(M, 1)
extend_mat(M, size(M,1), j)
else
if j < 1
extend_mat(M, i, 1)
elseif j > size(M, 2)
extend_mat(M, i, size(M,2))
else
M[i,j]
end
end
end
👀 Reading hidden code
908 μs

Hint

num_rows, num_columns = size(M)

👀 Reading hidden code
206 μs

Let's test it!

👀 Reading hidden code
213 μs
small_image = Gray.(rand(5,5))
👀 Reading hidden code
54.6 ms

Extended with 0:

👀 Reading hidden code
189 μs
👀 Reading hidden code
161 ms

Extended with your extend:

👀 Reading hidden code
186 μs
👀 Reading hidden code
35.1 ms

Got it!

Well done!

👀 Reading hidden code
148 μs
👀 Reading hidden code
36.2 ms

Exercise 4.2

👉 Implement a function convolve_image(M, K).

👀 Reading hidden code
236 μs
convolve_image (generic function with 1 method)
function convolve_image(M::AbstractMatrix, K::AbstractMatrix)
l = (size(K,1) - 1) ÷ 2
map(CartesianIndices(M)) do i
sum(CartesianIndices(K)) do a
offset = a - CartesianIndex(-l-1, -l-1)
extend_mat(M, (i - offset).I...) * K[a]
end
end
end
👀 Reading hidden code
2.0 ms

Hint

num_rows, num_columns = size(K)

👀 Reading hidden code
708 μs

Let's test it out! 🎃

👀 Reading hidden code
237 μs
👀 Reading hidden code
78.0 ms
3×3 Matrix{Float64}:
 0.0  0.0  0.0
 0.5  0.0  0.5
 0.0  0.0  0.0
K_test = [
0 0 0
1/2 0 1/2
0 0 0
]
👀 Reading hidden code
25.3 ms
convolve_image(test_image_with_border, K_test)
👀 Reading hidden code
79.7 ms

Edit K_test to create your own test case!

👀 Reading hidden code
285 μs
convolve_image(philip, K_test)
👀 Reading hidden code
11.1 ms

You can create all sorts of effects by choosing the kernel in a smart way. Today, we will implement two special kernels, to produce a Gaussian blur and a Sobel edge detect filter.

Make sure that you have watched the lecture about convolutions!

👀 Reading hidden code
427 μs

Exercise 4.3

👉 Apply a Gaussian blur to an image.

👀 Reading hidden code
312 μs

Hint

Can we just copy the 1D code? What is different in 2D?

👀 Reading hidden code
212 μs
with_gaussian_blur (generic function with 1 method)
function with_gaussian_blur(image; sigma=3, l=5)
gauss(x,y; sigma=3) = (1/(2*pi*sigma^2))exp(-(x^2 + y^2)/(2*sigma^2))
K_gauss = [gauss(xy...) for xy in Iterators.product(-l:l,-l:l)]
convolve_image(image, K_gauss ./ sum(K_gauss))
end
👀 Reading hidden code
4.4 ms

Let's make it interactive. 💫

👀 Reading hidden code
245 μs
Enable webcam
👀 Reading hidden code
45.5 ms
Error message

Another cell defining gauss_camera_image contains errors.

with_gaussian_blur(gauss_camera_image)
👀 Reading hidden code
---
Error message

MethodError: no method matching getindex(::Missing, ::String)

Stack trace

Here is what happened, the most recent locations are first:

  1. process_raw_camera_data(raw_camera_data::Missing)
    	# So to get the red values for each pixel, we take every 4th value, starting at 	# the 1st:	reds_flat = UInt8.(raw_camera_data["data"][1:4:end])	greens_flat = UInt8.(raw_camera_data["data"][2:4:end])	blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
  2. Show more...
gauss_camera_image = process_raw_camera_data(gauss_raw_camera_data);
👀 Reading hidden code
---

Exercise 4.4

👉 Create a Sobel edge detection filter.

👀 Reading hidden code
297 μs
with_sobel_edge_detect (generic function with 1 method)
function with_sobel_edge_detect(image)
K_sobol = [
1 0 -1
2 0 -2
1 0 -1
]
x = convolve_image(image, K_sobol)
y = convolve_image(image, K_sobol')
return x .* x .+ y .* y
end
👀 Reading hidden code
752 μs
Enable webcam
👀 Reading hidden code
746 μs
Error message

Another cell defining sobel_camera_image contains errors.

beep boop CRASH 🤖
with_sobel_edge_detect(sobel_camera_image)
👀 Reading hidden code
---
Error message

MethodError: no method matching getindex(::Missing, ::String)

Stack trace

Here is what happened, the most recent locations are first:

  1. process_raw_camera_data(raw_camera_data::Missing)
    	# So to get the red values for each pixel, we take every 4th value, starting at 	# the 1st:	reds_flat = UInt8.(raw_camera_data["data"][1:4:end])	greens_flat = UInt8.(raw_camera_data["data"][2:4:end])	blues_flat = UInt8.(raw_camera_data["data"][3:4:end])
  2. Show more...
sobel_camera_image = Gray.(process_raw_camera_data(sobel_raw_camera_data));
👀 Reading hidden code
---





👀 Reading hidden code
9.4 μs

Exercise 5 - Lecture transcript

(MIT students only)

Please see the Piazza post for transcript document here.

👉 Which lines of the transcripts did you edit?

👀 Reading hidden code
470 μs

Lecture 1, lines 10-50 (for example)

lines_i_edited = md"""
Lecture 1, lines 10-50 (_for example_)
"""
👀 Reading hidden code
337 μs





👀 Reading hidden code
8.7 μs
👀 Reading hidden code
285 μs





👀 Reading hidden code
9.8 μs
hint (generic function with 1 method)
👀 Reading hidden code
477 μs
almost (generic function with 1 method)
👀 Reading hidden code
444 μs
still_missing (generic function with 2 methods)
👀 Reading hidden code
912 μs
keep_working (generic function with 2 methods)
👀 Reading hidden code
908 μs
👀 Reading hidden code
12.2 ms
correct (generic function with 2 methods)
👀 Reading hidden code
864 μs
not_defined (generic function with 1 method)
👀 Reading hidden code
936 μs
👀 Reading hidden code
1.7 ms
camera_input (generic function with 1 method)
👀 Reading hidden code
1.4 ms
process_raw_camera_data (generic function with 1 method)
👀 Reading hidden code
1.4 ms